package com.starsky.common.util.easypoi;

import cn.afterturn.easypoi.word.WordExportUtil;
import cn.afterturn.easypoi.word.entity.MyXWPFDocument;
import com.starsky.common.util.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.apache.xmlbeans.XmlCursor;
import org.springframework.util.CollectionUtils;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * Excel工具类
 */
@Slf4j
public class WordUtils {

    public static final String CHARSET = "charset";

    public static final String UTF8 = "UTF-8";

    public static final String CONTENT_TYPE = "content-Type";

    public static final String APPLICATION_STREAM_UTF8 = "application/octet-stream;charset=utf-8";

    public static final String APPLICATION_VND_EXCEL = "application/vnd.ms-excel";

    public static final String TEXT_PLAN = "text/plain;charset=utf-8";

    public static final String CONTENT_DISPOSITION = "Content-Disposition";
    public static final String ATTACHMENT = "attachment;filename=";

    public static final String DOCX = ".docx";

    public static final String XLSX = ".xlsx";

    public static final String SQL = ".sql";

    public static final String TXT = ".txt";

    public static int USE_SXSSF_LIMIT = 100000;

    private WordUtils() {

    }

    /**
     * 获取下载路径
     *
     * @param filePath 文件全路径
     */
    public static String getAbsoluteFile(String filePath, String fileName) {
        File desc = new File(filePath + File.separator + fileName);
        if (!desc.getParentFile().exists()) {
            desc.getParentFile().mkdirs();
        }
        return filePath;
    }

    /**
     * 获取下载路径
     *
     * @param filePath 文件全路径
     */
    public static String getAbsoluteFile(String filePath) {
        File desc = new File(filePath);
        if (!desc.getParentFile().exists()) {
            desc.getParentFile().mkdirs();
        }
        return filePath;
    }


    public static String getAttachFileName(String fileName, String subfix) throws UnsupportedEncodingException {
        final String encode = URLEncoder.encode(fileName, UTF8);
        return ATTACHMENT + encode + subfix;
    }

    /**
     * Excel导出单个sheet
     *
     * @param response response
     * @param fileName 文件名
     * @param document 创建导出word
     */
    public static void writeResponse(HttpServletResponse response, XWPFDocument document, String fileName) {

        try {
            // 设置强制下载不打开
            response.setContentType("application/force-download");
            response.setCharacterEncoding("UTF-8");
            response.setHeader("content-Type", "application/octet-stream;charset=utf-8");
            response.setHeader("Content-Disposition",
                    "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".docx");
            ServletOutputStream out = response.getOutputStream();
            document.write(out);
            out.flush();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * Excel导出单个sheet
     *
     * @param response response
     * @param fileName 文件名
     * @param document 创建导出word
     */
    public static void writeResponseZip(HttpServletResponse response, XWPFDocument document, String fileName) {

        try {
            response.setCharacterEncoding("UTF-8");
            response.setHeader("content-Type", "application/octet-stream;charset=utf-8");
            response.setHeader("Content-Disposition",
                    "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".zip");
            ServletOutputStream out = response.getOutputStream();
            document.write(out);
            out.flush();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * Excel导出单个sheet
     *
     * @param out      response
     * @param document 创建导出word
     */
    public static void writeResponse(OutputStream out, XWPFDocument document) {

        try {
            document.write(out);
            out.flush();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Excel导出单个sheet
     *
     * @param templateUrl : word模板文件路径
     * @param map         数据
     */
    public static XWPFDocument exportWord07(String templateUrl, Map<String, Object> map) throws Exception {
        XWPFDocument doc = WordExportUtil.exportWord07(templateUrl, map);
        return doc;
    }


    /**
     * Excel导出单个sheet
     *
     * @param response response
     * @param fileName 文件名
     * @param map      数据
     */
    public static void exportWord07(HttpServletResponse response,
                                    String templateUrl,
                                    String fileName,
                                    Map<String, Object> map) {
        try {
            if (StringUtils.isBlank(fileName)) {
                // 当前日期
                fileName = DateUtils.format(new Date(), null);
            }

            XWPFDocument doc = WordExportUtil.exportWord07(templateUrl, map);
            WordUtils.writeResponse(response, doc, fileName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * Excel导出单个sheet
     *
     * @param response response
     * @param fileName 文件名
     * @param list     数据
     */
    public static void exportWord07(HttpServletResponse response,
                                    String templateUrl,
                                    String fileName,
                                    List<Map<String, Object>> list) {
        try {
            if (StringUtils.isBlank(fileName)) {
                // 当前日期
                fileName = DateUtils.format(new Date(), null);
            }

            XWPFDocument doc = WordExportUtil.exportWord07(templateUrl, list);
            WordUtils.writeResponse(response, doc, fileName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Excel导出单个sheet
     *
     * @param response response
     * @param fileName 文件名
     * @param map      数据
     */
    public static void exportWord07(HttpServletResponse response,
                                    XWPFDocument document,
                                    String fileName,
                                    Map<String, Object> map) {
        try {
            if (StringUtils.isBlank(fileName)) {
                // 当前日期
                fileName = DateUtils.format(new Date(), null);
            }
            WordExportUtil.exportWord07(document, map);
            WordUtils.writeResponse(response, document, fileName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * Excel导出单个sheet
     *
     * @param templateUrl 模板文件路径
     * @param outPath     文件输出路径
     * @param map         数据
     */
    public static void exportWord07(String templateUrl, String outPath, Map<String, Object> map) {
        try {
            outPath = getAbsoluteFile(outPath);
            XWPFDocument doc = WordExportUtil.exportWord07(templateUrl, map);
            WordUtils.writeResponse(new FileOutputStream(outPath), doc);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * Excel导出单个sheet
     * springboot使用easypoi根据模板导出word。解决本地可以导出。打包到服务器导出报错的问题
     * 在本地测试使用IDEA运行都是可以导出的。但是打包以后就报如下错误：
     * cn.afterturn.easypoi.cache.WordCache : java.lang.NullPointerException
     * <p>
     * 具体错误原因是路径不对。打包以后。springboot的路径变为： xxx.jar/BOOT-INF/classes/simpleWord.docx。
     *
     * @param in:            模板文件路径
     * @param fileName:下载文件名
     * @param map：数据
     */
    public static void exportWord07(HttpServletResponse response, InputStream in, Map<String, Object> map, String fileName) {
        try {
            //使用文件流方式
//            InputStream is = this.getClass().getResourceAsStream("/word/export.docx");
            XWPFDocument doc = new MyXWPFDocument(in);
            WordExportUtil.exportWord07(doc, map);
            WordUtils.writeResponse(response, doc, fileName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 导出word
     *
     * @param response
     * @param templateUrl: word模版路径
     * @param fileName：    导出文件名称
     * @param map：         word填充数据
     * @param map：         word表格填充数据
     * @param dataList：    word表格填充列，和数据对应用
     *                     格式：
     *                     [
     *                     {
     *                     "titles": ['标题1','标题2','标题3'],
     *                     "columns": ['标题1对应的列名称','标题2对应的列名称','标题3对应的列名称'],
     *                     "data": [{'标题1对应的列数据','标题2对应的列数据','标题3对应的列数据'}]
     *                     }
     *                     ]
     */
    public static void exportWord07(HttpServletResponse response,
                                    String templateUrl,
                                    String fileName,
                                    String outPath,
                                    Map<String, Object> map,
                                    List<Map<String, Object>> dataList) {
        try {
            response.setCharacterEncoding(UTF8);
            response.setHeader(CONTENT_TYPE, APPLICATION_STREAM_UTF8);
            response.setHeader(CONTENT_DISPOSITION, getAttachFileName(fileName, DOCX));

            final String path = outPath + "/" + fileName + DOCX;
            OutputStream out = new FileOutputStream(new File(path));
            XWPFDocument document = WordExportUtil.exportWord07(templateUrl, map);
            List<XWPFParagraph> paragraphs = document.getParagraphs();

            final int size = paragraphs.size();
            for (int i = 0; i < size; i++) {
                final XWPFParagraph xwpfParagraph = paragraphs.get(i);
                final String text = xwpfParagraph.getText();
                //第一行为标题头信息
                String[] titles = {};
                //第二行开始为数据
                String[] columnNames = {};
                createTable(document, xwpfParagraph, dataList, Arrays.asList(titles), Arrays.asList(columnNames));
            }
            document.write(out);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 创建表格
     *
     * @param document
     * @param xwpfParagraph
     * @param list：表数据
     * @param titles：标题头
     * @param columnNames：列名称
     */
    public static void createTable(XWPFDocument document, XWPFParagraph xwpfParagraph,
                                   List<Map<String, Object>> list,
                                   List<String> titles,
                                   List<String> columnNames) {
        if (CollectionUtils.isEmpty(list) //
                || CollectionUtils.isEmpty(titles) //
                || CollectionUtils.isEmpty(columnNames)) {
            return;
        }

        final XWPFTable table = getXWPFTable(document, xwpfParagraph);
        addTitleCell(table, titles);
        addRow(table, list, columnNames);
        //将制定位置原始替换
        document.removeBodyElement(document.getPosOfParagraph(xwpfParagraph));
    }

    /**
     * 创建xml游标位置
     *
     * @param xwpfParagraph
     * @return
     */
    public static XmlCursor getXmlCursor(XWPFParagraph xwpfParagraph) {
        XmlCursor xmlCursor = xwpfParagraph.getCTP().newCursor();
        return xmlCursor;
    }

    /**
     * 创建表格
     *
     * @param document
     * @param xwpfParagraph
     * @return
     */
    public static XWPFTable getXWPFTable(XWPFDocument document, XWPFParagraph xwpfParagraph) {
        XmlCursor xmlCursor = getXmlCursor(xwpfParagraph);
        XWPFTable table = document.insertNewTbl(xmlCursor);
        return table;
    }


    /**
     * 创建标题头
     *
     * @param table
     * @param titles
     */
    public static void addTitleCell(XWPFTable table, List<String> titles) {
        if (CollectionUtils.isEmpty(titles)) {
            return;
        }
        final XWPFTableRow row = table.getRow(0);
        for (int i = 0; i < titles.size(); i++) {
            final String title = titles.get(i);
            if (i == 0) {
                row.getCell(0).setText(title);
            } else {
                row.addNewTableCell().setText(title);
            }
        }
    }

    /**
     * 创建标题头
     *
     * @param table:          表格
     * @param list：           行数据
     * @param columnNames：列名称
     */
    public static void addRow(XWPFTable table, List<Map<String, Object>> list, List<String> columnNames) {
        if (CollectionUtils.isEmpty(list) || CollectionUtils.isEmpty(columnNames)) {
            return;
        }

        for (Map<String, Object> map : list) {
            List<String> cellValues = getCellValues(map, columnNames);
            addCell(table, cellValues);
        }
    }

    /**
     * 添加单元格数据
     *
     * @param table：table表格
     * @param cellDataList： 单元格数据
     */
    public static void addCell(XWPFTable table, List<String> cellDataList) {
        if (CollectionUtils.isEmpty(cellDataList)) {
            return;
        }
        final XWPFTableRow row = table.createRow();
        for (int i = 0; i < cellDataList.size(); i++) {
            final String text = cellDataList.get(i);
            row.getCell(i).setText(text);
        }
    }

    /**
     * 获取单元格数据
     *
     * @param map：数据
     * @param columnNames：列名称
     * @return
     */
    public static List<String> getCellValues(Map<String, Object> map, List<String> columnNames) {
        List<String> cellValues = new ArrayList<>();
        if (map == null || map.size() <= 0 || CollectionUtils.isEmpty(columnNames)) {
            return cellValues;
        }

        for (String columnName : columnNames) {
            final String text = map.get(columnName) != null ? map.get(columnName).toString() : "";
            cellValues.add(text);
        }
        return cellValues;
    }


    /**
     * Excel导出单个sheet
     *
     * @param response response
     * @param fileName 文件名
     */
    public static void setZipResponseHeader(HttpServletResponse response, String fileName) {

        try {
            final String zipFileName = URLEncoder.encode(fileName, "UTF-8") + ".zip";
            // 设置强制下载不打开
            response.setContentType("application/force-download");
            response.setCharacterEncoding("UTF-8");
//        response.setHeader("content-Type", "application/octet-stream;charset=utf-8");
            response.setHeader("Content-Type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
            response.setHeader("Content-Disposition", "attachment;filename=" + zipFileName);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 写入压缩文件
     *
     * @param doc
     * @param zos
     * @param fileName
     * @throws IOException
     */
    public static void write(XWPFDocument doc, ZipOutputStream zos, String fileName) throws IOException {
        zos.putNextEntry(new ZipEntry(fileName));
        doc.write(zos);
        doc.close();
        zos.flush();
    }

    /**
     * 写入压缩文件
     *
     * @param doc
     * @param out
     * @param fileName
     * @throws IOException
     */
    public static void write(XWPFDocument doc, OutputStream out, String fileName) throws IOException {
        ZipOutputStream zos = new ZipOutputStream(out);
        zos.putNextEntry(new ZipEntry(fileName));
        doc.write(zos);
        doc.close();

        zos.flush();
        zos.close();
        out.flush();
    }

    /**
     * Excel导出单个sheet
     *
     * @param response response
     * @param doc      创建导出word
     * @param fileName 文件名
     */
    public static void writeZipResponse(HttpServletResponse response, XWPFDocument doc, String fileName) {
        try {
            WordUtils.setZipResponseHeader(response, fileName);
            ServletOutputStream out = response.getOutputStream();
            WordUtils.write(doc, out, fileName);
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 导出压缩文件
     *
     * @param templateUrl：word模板文件路径
     * @param map：                   数据
     * @param fileName：              文件名，自定义即可
     */
    public static void exportWord07Zip(HttpServletResponse response, String templateUrl, Map<String, Object> map, String fileName, String zipFileName) throws Exception {
        setZipResponseHeader(response, fileName);

        ServletOutputStream out = response.getOutputStream();
        XWPFDocument doc = WordExportUtil.exportWord07(templateUrl, map);
        WordUtils.write(doc, out, fileName);
        out.flush();
        out.close();
    }


    /**
     * 导出压缩文件
     *
     * @param templateUrl：word模板文件路径
     * @param map：                   数据
     * @param fileName：              文件名，自定义即可
     * @param zipFileName：导出压缩文件名
     */
    public static void exportWord07Zip(String templateUrl, Map<String, Object> map, String fileName, String zipFileName) throws Exception {
        XWPFDocument doc = WordExportUtil.exportWord07(templateUrl, map);
        FileOutputStream out = new FileOutputStream(zipFileName);
        WordUtils.write(doc, out, fileName);
        out.close();
    }


    /**
     * 导出压缩文件
     *
     * @param map：                数据,key-文名称；value-doc文档
     * @param zipFileName：导出压缩文件名
     */
    public static void exportWord07Zip(Map<String, XWPFDocument> map, String zipFileName) throws Exception {
        // 压缩文件
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFileName));
        for (String fileName : map.keySet()) {
            final XWPFDocument doc = map.get(fileName);
            WordUtils.write(doc, zos, fileName);
        }
        zos.close();
    }


    /**
     * 导出压缩文件
     *
     * @param map：                数据,key-文名称；value-doc文档
     * @param zipFileName：导出压缩文件名
     */
    public static void exportWord07Zip(HttpServletResponse response, Map<String, XWPFDocument> map, String zipFileName) throws Exception {
        setZipResponseHeader(response, zipFileName);
        // 压缩文件
        ServletOutputStream out = response.getOutputStream();
        ZipOutputStream zos = new ZipOutputStream(out);
        for (String fileName : map.keySet()) {
            final XWPFDocument doc = map.get(fileName);
            WordUtils.write(doc, zos, fileName);
        }
        zos.close();
        out.flush();
        out.close();
    }


    /**
     * 简单导出没有图片和Excel
     */
    public static void SimpleWordExport() {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("department", "Easypoi");
        map.put("person", "JueYue");
        map.put("time", "2022-07-10");
        map.put("me", "JueYue");
        map.put("date", "2015-01-03");
        map.put("mark", "0x0051");
        try {
            XWPFDocument doc = WordExportUtil.exportWord07("E:word/Simple.docx", map);
            FileOutputStream fos = new FileOutputStream("E:/home/excel/simple.docx");
            doc.write(fos);
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
